﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLite.Reflection;
using NLite.Data.Internal;

namespace NLite.Data
{
    public static class ColumnRewriter 
    {
        public static SelectNode Rewrite(SelectNode select)
        {
            return SelectColumnRewrite.Rewrite(select);
        }

        public static InsertNode Rewrite(InsertNode select)
        {
            return new InternalColumnRewrite().Visit(select) as InsertNode;
        }

        public static DeleteNode Rewrite(DeleteNode select)
        {
            return new InternalColumnRewrite().Visit(select) as DeleteNode;
        }

        public static UpdateNode Rewrite(UpdateNode select)
        {
            return new InternalColumnRewrite().Visit(select) as UpdateNode;
        }
    }

    namespace Internal
    {
        class SingleTableChecker : NodeVisitor
        {
            private bool _IsSingleTable;
            protected override SelectNode VisitSelect(SelectNode select)
            {
                _IsSingleTable = select.Joins.Count == 0;
                return select;
            }

            public static bool Check(SelectNode select)
            {
                var o = new SingleTableChecker();
                o.VisitSelect(select);
                return o._IsSingleTable;
            }
        }

        class SelectColumnRewrite : NodeVisitor
        {
            private bool IsSingleTable;
            SelectNode Node;
            Dictionary<string, AliasNode> AliasProjections;
            List<IColumnNode> Columns;

            public static SelectNode Rewrite(SelectNode select)
            {
                return new SelectColumnRewrite().VisitSelect(select);
            }

            protected override SelectNode VisitSelect(SelectNode select)
            {
                IsSingleTable = SingleTableChecker.Check(select);
                Node = select;
                AliasProjections = new Dictionary<string, AliasNode>(StringComparer.InvariantCultureIgnoreCase);
                Columns = new List<IColumnNode>();

                select = base.VisitSelect(select);


                if (AliasProjections.Count > 0)
                {
                    select.Projections.Clear();
                    foreach (var item in AliasProjections.Values)
                        select.Projections.Add(item);
                }

                return select;
            }

            protected override AliasNode VisitAlias(AliasNode exp)
            {
                exp = base.VisitAlias(exp);
                AliasProjections.Add(exp.Alias, exp);
                return exp;
            }

            protected override IColumnNode VisitColumn(IColumnNode column)
            {
                if (IsSingleTable && !string.IsNullOrEmpty(column.Table))
                    column.SetProperty("Table", null);
                else if (string.Equals(column.Table, Node.Name, StringComparison.InvariantCultureIgnoreCase))
                    column.SetProperty("Table", null);
                else
                {
                    var join = Node.Joins.FirstOrDefault(p => string.Equals(column.Table, p.Name, StringComparison.OrdinalIgnoreCase)
                                    || string.Equals(column.Identity.Name, p.Alias, StringComparison.OrdinalIgnoreCase));

                    if (join == null)
                        return column;

                    var foundTableName = string.IsNullOrEmpty(join.Alias) ? join.Name : join.Alias;
                    if (!string.IsNullOrEmpty(join.Alias)
                        && !string.Equals(column.Table, join.Alias, StringComparison.InvariantCultureIgnoreCase))
                        column.SetProperty("Table", join.Alias);
                }
                return base.VisitColumn(column);
            }
        }

        class InternalColumnRewrite : NodeVisitor
        {
           

            protected override IColumnNode VisitColumn(IColumnNode column)
            {
                column.SetProperty("Table", null);
                return base.VisitColumn(column);
            }
        }
    }
}
